/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package grafogen;

import java.io.Serializable;
import java.util.ArrayList;


/**
 *
 * @author laura
 */
public class Grafo implements Serializable, Cloneable {
    private ArrayList<Nodo> nodos;
    private ArrayList<Arista> aristas;
    private Nodo ultimoNodo;
    private Integer cantidadNodos;
    private Integer longCadenaCortada;
    private boolean union;
    private Integer cantidadCausas;

    /**
    * Constructor de la clase Grafo para crear un grafo con nodos y aristas 
    */
    public Grafo() {
            nodos = new ArrayList<Nodo>();
            aristas = new ArrayList<Arista>();
            union = false; //inicialmente es un grafo independiente
    }
    
    /**
     * Método que permite agregar una arista a la colección de aristas
     * @param arista
     *                              Arista a agregar
     * @return  true si se agregó la arista, false si hubo algún error
     */
    public boolean agregarArista(Arista arista) {
            return getAristas().add(arista);
    }
    
    /**
     * Método que permite agregar un nodo a la colección de nodos
     * @param nodo
     *                              Nodo a agregar
     * @return  true si se agregó el nodo, false si hubo algún error
     */
    public boolean agregarNodo(Nodo nodo) {
            return getNodos().add(nodo);
    } 
    
    /**
     * @return the nodos
     */
    public ArrayList<Nodo> getNodos() {
        return nodos;
    }

    /**
     * @param nodos the nodos to set
     */
    public void setNodos(ArrayList<Nodo> nodos) {
        this.nodos = nodos;
    }

    /**
     * @return the aristas
     */
    public ArrayList<Arista> getAristas() {
        return aristas;
    }

    /**
     * @param aristas the aristas to set
     */
    public void setAristas(ArrayList<Arista> aristas) {
        this.aristas = aristas;
    }

    /**
     * @return the ultimoNodo
     */
    public Nodo getUltimoNodo() {
        return ultimoNodo;
    }

    /**
     * @param ultimoNodo the ultimoNodo to set
     */
    public void setUltimoNodo(Nodo ultimoNodo) {
        this.ultimoNodo = ultimoNodo;
    }


    public Grafo armarGrafo(String cadena){
        Grafo grafo = new Grafo();    //si encontre parentesis abierto y grafo no fue creado, lo creo por primera vez
                 

        String [] arrayCadena = cadena.split(" "); //partimos cadena por espacios en blanco

        int longCadena = 0;

        for(int i=0; i < arrayCadena.length; i++){
            String palabra = arrayCadena[i];
            Integer cantAristas = 0;
            Integer cantNodos = 0;

            if("(".equals(palabra)){
                //si es parentesis abierto, siempre crear grafo
                
                    //recorto lo ya recorrido
                    cadena = cadena.substring(palabra.length()+1); //borro el parentesis y el espacio en blanco que viene despues de el
                    longCadena += palabra.length() + 1;    //acumulo lo que corte
                    arrayCadena = cadena.split(" ");
                    i=-1; //el siguiente recorrido ya inicia sumando 1, si quiero empezar en cero debo poner i a -1
                
                    //si el grafo ya fue creado, es porque encontre otro parentesis abierto en la cadena
                    //llamo recursivamente pasandole la cadena sin la parte que ya fue evaluada
                    //int posicionCorte = palabra.length()+1; //considerando que al crear un nodo, ya sacamos del array y de la cadena
                    // + 1 para quitar el espacio tambien

                    //cadena = cadena.substring(posicionCorte);
//no debo cortar, debe ir la cadena con el parentesis abierto
                    
                //if (grafo.getNodos().size() > 0) {
                    Grafo grafo2 = armarGrafo(cadena); //creo un nuevo grafo con el resto de la cadena

                    //al retornar anexar grafo2 al grafo
                    if (grafo2 != null) {
                        if (grafo.getNodos().size() > 0) {
                            cantAristas = grafo.getAristas().size();
                            Arista arista = new Arista(cantAristas + 1, grafo2.getUltimoNodo(), grafo.getUltimoNodo());
                            //el ultimo nodo del grafo2 debe ser un operador y 
                            //se unira al ultimoNodo del grafo, que tb es un operador
                            grafo.agregarArista(arista);
                        } else {
                            //el ultimo nodo del grafo2 sera el ultimo nodo del grafo padre, si este no tiene nodos
                            grafo.setUltimoNodo(grafo2.getUltimoNodo());
                        }

                        //debo agregar todos los nodos y aristas del grafo2 a grafo
                        grafo.addAristas(grafo2.getAristas());
                        grafo.addNodos(grafo2.getNodos());


                        //debo sacar de la cadena la parte ya evaluada, 
                        //es decir la suma de la longitud del texto de cada nodo, 
                        //del grafo2 + 1 por el parentesis cerrado
                        int longTextoCortar = grafo2.getLongCadenaCortada();
                        longCadena += grafo2.getLongCadenaCortada();
                        //contar cuantos parentesis abiertos tiene y cuantos cerrados y sumar a longTextoCortar
                        //por cada parentesis encontrado, sumarle 1
                        cadena = cadena.substring(longTextoCortar);
                        arrayCadena = cadena.split(" ");
                        i = -1;//el siguiente recorrido ya inicia sumando 1, si quiero empezar en cero debo poner i a -1

                    }
                //}
                    
                    
                
            }else if ( !(")".equals(palabra)) ){    //si no es parentesis cerrado
                cantNodos = grafo.getNodos().size();
                boolean esOperador = "and".equals(palabra)?true:("or".equals(palabra)?true:false);
                Nodo nodo = new Nodo(cantNodos+1, palabra, null);

                if(esOperador){
                    nodo.setTipo("operador");
                }else{
                    nodo.setTipo("causa");
                }

                grafo.agregarNodo(nodo); //agregar nodo al grafo

                if(grafo.getUltimoNodo() != null){
                    cantAristas = grafo.getAristas().size();
                    Arista arista = null;

                    if(esOperador){
                           arista = new Arista(cantAristas+1, grafo.getUltimoNodo(), nodo);
                           grafo.setUltimoNodo(nodo); //el ultimo nodo debe ser un operador
                    }else{ 
                        if("operador".equals(grafo.getUltimoNodo().getTipo())){ //ultimo nodo es operador
                           arista = new Arista(cantAristas+1, nodo, grafo.getUltimoNodo() );
                        }
                    }
                    //agregar arista al grafo
                    grafo.agregarArista(arista);

                }else{
                    grafo.setUltimoNodo(nodo);
                }
                //recortamos la cadena

                cadena = cadena.substring(palabra.length() + 1); //borro el parentesis y el espacio en blanco que viene despues de el
                longCadena += palabra.length() + 1;
                arrayCadena = cadena.split(" ");
                i = -1; //el siguiente recorrido ya inicia sumando 1, si quiero empezar en cero debo poner i a -1

            }else{  //es parentesis cerrado
                //si es un parentesis cerrado y es el unico elemento del 
                //arrayCadena es el fin de todo, entonces no sumo 1 porque no hay espacio (creo)
                if(arrayCadena.length > 1){
                    longCadena += palabra.length() + 1; //no es el fin de todo
                }
                    
                grafo.setLongCadenaCortada(longCadena);
                return grafo;
            }
        }

        return null;
    }



   /**
     * @return the cantidadNodos
     */
    public Integer getCantidadNodos() {
        return cantidadNodos;
    }

    /**
     * @param cantidadNodos the cantidadNodos to set
     */
    public void setCantidadNodos(Integer cantidadNodos) {
        this.cantidadNodos = cantidadNodos;
    }


    public void addAristas(ArrayList<Arista> aristas){
        for(Arista arista : aristas){
            this.agregarArista(arista);
        }
    }


    public void addNodos(ArrayList<Nodo> nodos){
        for(Nodo nodo : nodos){
            this.agregarNodo(nodo);
        }
    }


    public int cantidadDeParentesis(String cadena){
        int cantidad = 0;
        for(int i=0; i < cadena.length(); i++){
            if( '(' == cadena.charAt(i) ){
                cantidad = cantidad + 2; //siempre despues de un parentesis abierto hay un espeacio en blanco
            }else if ( ')' == cadena.charAt(i) ){
                if (i + 1 < cadena.length()) {
                    cantidad = cantidad + 2; //si es un parentesis cerrado, pero no es el ultimo, entonces hay un espacio en blanco despues
                } else {
                    cantidad = cantidad + 1; //el ultimo parentesis cerrado no tiene espacio en blanco despues
                }

            }
        }

        return cantidad;
    }

    /**
     * @return the longCadenaCortada
     */
    public Integer getLongCadenaCortada() {
        return longCadenaCortada;
    }

    /**
     * @param longCadenaCortada the longCadenaCortada to set
     */
    public void setLongCadenaCortada(Integer longCadenaCortada) {
        this.longCadenaCortada = longCadenaCortada;
    }


    

    public boolean existeNodo(Nodo nodo){
        boolean existe = false;

        if(this.getNodos().size() > 0){
            for(Nodo nodoAux :this.getNodos()){
                if (nodoAux.getDescripcion().equals(nodo.getDescripcion())){
                    existe = true;
                    return existe;
                }
            }
        }

        return existe;
    }

    
    
    
    
    

    public ArrayList<Arista> buscarAristaDest(Nodo nodo){
        ArrayList<Arista> aristasDest= new ArrayList<Arista>();
        ArrayList<Arista> aristasGrafo = new ArrayList<Arista>();

        aristasGrafo = this.getAristas();

        for(int i=0; i < aristasGrafo.size(); i++){
            Arista arista = aristasGrafo.get(i);
            
            if("operador".equals(arista.getDestino().getTipo())){
                if(arista.getDestino().equals(nodo)){
                    aristasDest.add(arista);
                }
            }else{
                if(arista.getDestino().getDescripcion().equals(nodo.getDescripcion())){
                    aristasDest.add(arista);
                }    
            }
            
            
        }

        return aristasDest;
    }

    /**
     * @return the union
     */
    public boolean isUnion() {
        return union;
    }

    /**
     * @param union the union to set
     */
    public void setUnion(boolean union) {
        this.union = union;
    }
    
    public Grafo clone(){
        Grafo obj=null;
        try{
            obj=(Grafo)super.clone();
        }catch(CloneNotSupportedException ex){
            System.out.println(" no se puede duplicar");
        }
        return obj;
    }

    public static ArrayList<Grafo> cloneList(ArrayList<Grafo> list) {
        ArrayList<Grafo> clone = new ArrayList<Grafo>(list.size());
        for(Grafo item: list){
            Grafo g = new Grafo();
            g.addAristas(item.getAristas());
            g.addNodos(item.getNodos());
            g.setUltimoNodo(item.getUltimoNodo());
            clone.add(g);
        }
        return clone;
    }

    //busca aristas que tengan como destino un efecto
    public ArrayList<Arista> buscarAristaDestEfecto(){
        ArrayList<Arista> aristasDest= new ArrayList<Arista>();
        ArrayList<Arista> aristasGrafo = new ArrayList<Arista>();

        aristasGrafo = this.getAristas();
try{
        for(int i=0; i < aristasGrafo.size(); i++){
            Arista arista = aristasGrafo.get(i);
            if("efecto".equals(arista.getDestino().getTipo()) ){
                aristasDest.add(arista);
            }
        }
}catch(Exception e){
    e.printStackTrace();
}
        return aristasDest;
    }

    /**
     * @return the cantidadCausas
     */
    public Integer getCantidadCausas() {
        return cantidadCausas;
    }

    /**
     * @param cantidadCausas the cantidadCausas to set
     */
    public void setCantidadCausas(Integer cantidadCausas) {
        this.cantidadCausas = cantidadCausas;
    }

//    public String toString(){
//        String grafoEnCadena = "";
//        
//        for(Arista arista : this.getAristas()){
//            
//            String seudonimoOri = obtenerSeudonimo(arista.getOrigen().getDescripcion());
//            String seudonimoDes = obtenerSeudonimo(arista.getDestino().getDescripcion());
//
//            grafoEnCadena += seudonimoOri+"->"+seudonimoDes+";";
//        }
//        
//        return grafoEnCadena;
//    }
    
    
    public static String obtenerSeudonimo(String descripcion){
        String seudo = "";
        
        if("carinsurance.getSexo()==\"F\"".equals(descripcion)){
            seudo = "C1";
        }
        else if("carinsurance.getEdad()<65".equals(descripcion)){
            seudo = "C2";
        }
        else if("carinsurance.getSexo()==\"M\"".equals(descripcion)){
            seudo = "C3";
        }
        else if("carinsurance.getEdad()<25".equals(descripcion)){
            seudo = "C4";
        }
        else if("carinsurance.getEdad()>=25".equals(descripcion)){
            seudo = "C5";
        }
        else if("carinsurance.getEdad()>=65".equals(descripcion)){
            seudo = "C6";
        }
        else if("carinsurance.getSexo()!=\"M\"".equals(descripcion)){
            seudo = "C7";
        }
        else if("carinsurance.getSexo()!=\"F\"".equals(descripcion)){
            seudo = "C8";
        }
        else if("carinsurance.getEdad()<0".equals(descripcion)){
            seudo = "C9";
        }
        else if("carinsurance.getPremio()<0".equals(descripcion)){
            seudo = "C10";
        }
        else if("carinsurance.setPremio(500)".equals(descripcion)){
            seudo = "E1";
        }
        else if("carinsurance.setPremio(3000)".equals(descripcion)){
            seudo = "E2";
        }
        else if("carinsurance.setPremio(1000)".equals(descripcion)){
            seudo = "E3";
        }
        else if("carinsurance.setPremio(1500)".equals(descripcion)){
            seudo = "E4";
        }
        else if("carinsurance.printErrorMsg()".equals(descripcion)){
            seudo = "E5";
        }else{
            seudo = descripcion;
        }
              
        return seudo;
    }
//    
    /**
     * retorna la lista de indices de aristas,
     * que tienen como participante al nodo recibido como parametro
     * @param nodo
     * @return 
     */
    public ArrayList<Integer> obtenerAristasRelacionadas(Nodo nodo){
        ArrayList<Integer> listaAristas = new ArrayList<Integer>();
        int c = 0;
        for(Arista a: this.getAristas()){
            
            if("operador".equals(nodo.getTipo()) ){
                if (a.getOrigen().equals(nodo) || a.getDestino().equals(nodo)) {
                    listaAristas.add(c); //guarda el indice de la arista en el arrayList de aristas
                }
                
            }else{
                if(a.getOrigen().getDescripcion().equals(nodo.getDescripcion()) || a.getDestino().getDescripcion().equals(nodo.getDescripcion())){
                    listaAristas.add(c); //guarda el indice de la arista en el arrayList de aristas
                }    
            }

            c++;
        }
        
        return listaAristas;
    }
    
    
    public void reenumerarNodos() {
        int contOpe = 0;
        int contCau = 0;
        int contEfe = 0;
        for (Nodo nodo : this.getNodos()) {

            if ("operador".equalsIgnoreCase(nodo.getTipo())) {

                ArrayList<Integer> aristasRelacionadas = this.obtenerAristasRelacionadas(nodo);
                

                for (int indice = 0; indice < aristasRelacionadas.size(); indice++) {

                    Integer valorIndice = aristasRelacionadas.get(indice);

                    if (this.getAristas().get(valorIndice.intValue()).getDestino().equals(nodo)) {
                        this.getAristas().get(valorIndice.intValue()).getDestino().setCodigoN(contOpe);
                    } else {
                        this.getAristas().get(valorIndice.intValue()).getOrigen().setCodigoN(contOpe);
                    }
                }

                nodo.setCodigoN(contOpe);
                contOpe = contOpe + 1;

            } else if ("causa".equalsIgnoreCase(nodo.getTipo())) {

                ArrayList<Integer> aristasRelacionadas = this.obtenerAristasRelacionadas(nodo);
                
                for (Integer indice : aristasRelacionadas) {
                    if (this.getAristas().get(indice.intValue()).getDestino().getDescripcion().equals(nodo.getDescripcion())) {
                        this.getAristas().get(indice.intValue()).getDestino().setCodigoN(contCau);
                    } else {
                        this.getAristas().get(indice.intValue()).getOrigen().setCodigoN(contCau);
                    }
                }

                nodo.setCodigoN(contCau);
                contCau = contCau + 1;
            
            }else if ("efecto".equalsIgnoreCase(nodo.getTipo())) {
                
                ArrayList<Integer> aristasRelacionadas = this.obtenerAristasRelacionadas(nodo);
                
                for (Integer indice : aristasRelacionadas) {
                    if (this.getAristas().get(indice.intValue()).getDestino().getDescripcion().equals(nodo.getDescripcion())) {
                        this.getAristas().get(indice.intValue()).getDestino().setCodigoN(contEfe);
                    } else {
                        this.getAristas().get(indice.intValue()).getOrigen().setCodigoN(contEfe);
                    }
                }

                nodo.setCodigoN(contEfe);
                contEfe = contEfe + 1;
                
            }

        }

    }
    
    
}
